{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 任意选一个你喜欢的整数，这能帮你得到稳定的结果\n",
    "seed = 9999"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 欢迎来到线性回归项目\n",
    "\n",
    "若项目中的题目有困难没完成也没关系，我们鼓励你带着问题提交项目，评审人会给予你诸多帮助。\n",
    "\n",
    "所有选做题都可以不做，不影响项目通过。如果你做了，那么项目评审会帮你批改，也会因为选做部分做错而判定为不通过。\n",
    "\n",
    "其中非代码题可以提交手写后扫描的 pdf 文件，或使用 Latex 在文档中直接回答。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1 矩阵运算\n",
    "\n",
    "## 1.1 创建一个 4*4 的单位矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 这个项目设计来帮你熟悉 python list 和线性代数\n",
    "# 你不能调用任何NumPy以及相关的科学计算库来完成作业\n",
    "\n",
    "\n",
    "# 本项目要求矩阵统一使用二维列表表示，如下：\n",
    "A = [[1,2,3], \n",
    "     [2,3,3], \n",
    "     [1,2,5]]\n",
    "\n",
    "B = [[1,2,3,5], \n",
    "     [2,3,3,5], \n",
    "     [1,2,5,1]]\n",
    "\n",
    "# 向量也用二维列表表示\n",
    "C = [[1],\n",
    "     [2],\n",
    "     [3]]\n",
    "\n",
    "#TODO 创建一个 4*4 单位矩阵\n",
    "I = None\n",
    "I = [\n",
    "    [-1.3258, 3.6845, 10.68135, 11.56154],\n",
    "    [-6.32455, -5.92435, 12.64235, 15.62382],\n",
    "    [8.54655, 17.82645, -20.43512, 9.45783],\n",
    "    [18.84653, 11.28945, 8.63421, 7.58153]]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2 返回矩阵的行数和列数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO 返回矩阵的行数和列数\n",
    "def shape(M):\n",
    "    if not isinstance(M[0], list):\n",
    "        row = 1\n",
    "        col = 0\n",
    "    else:\n",
    "        row, col = len(M), len(M[0])\n",
    "    return row,col\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.044s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 shape 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.3 每个元素四舍五入到特定小数数位"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO 每个元素四舍五入到特定小数数位\n",
    "# 直接修改参数矩阵，无返回值\n",
    "def matxRound(M, decPts=4):\n",
    "    rows_M, cols_M = shape(M)\n",
    "    for row in range(rows_M):\n",
    "        for col in range(cols_M):\n",
    "            val = M[row][col]\n",
    "            M[row][col] = round(val, decPts)\n",
    "    pass\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.017s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 matxRound 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_matxRound"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.4 计算矩阵的转置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO 计算矩阵的转置\n",
    "def transpose(M):\n",
    "    MT = []\n",
    "    rows_M, cols_M = shape(M)\n",
    "    for col in range(cols_M):\n",
    "        temp = []\n",
    "        for row in range(rows_M):\n",
    "             val = M[row][col]\n",
    "             temp.append(val)                \n",
    "        MT.append(temp)\n",
    "    return MT\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.016s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 transpose 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_transpose"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.5 计算矩阵乘法 AB"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO 计算矩阵乘法 AB，如果无法相乘则raise ValueError\n",
    "def matxMultiply(A, B):\n",
    "    rows_A, cols_A = shape(A)\n",
    "    rows_B, cols_B = shape(B)\n",
    "\n",
    "    if not cols_A == rows_B:\n",
    "        raise ValueError(\"Matrix A's columns must be equal to B's rows\")\n",
    "\n",
    "    #创建一个0矩阵，列数等于B的列数，行数等于A的行数\n",
    "    result = [[0] * cols_B for i in range(rows_A)]\n",
    "    for i in range(rows_A):\n",
    "        for j in range(cols_B):\n",
    "            for k in range(rows_B):\n",
    "                result[i][j] += A[i][k]*B[k][j]\n",
    "\n",
    "    return result\n",
    "   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.111s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 matxMultiply 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_matxMultiply"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "# 2 Gaussign Jordan 消元法\n",
    "\n",
    "## 2.1 构造增广矩阵\n",
    "\n",
    "$ A = \\begin{bmatrix}\n",
    "    a_{11}    & a_{12} & ... & a_{1n}\\\\\n",
    "    a_{21}    & a_{22} & ... & a_{2n}\\\\\n",
    "    a_{31}    & a_{22} & ... & a_{3n}\\\\\n",
    "    ...    & ... & ... & ...\\\\\n",
    "    a_{n1}    & a_{n2} & ... & a_{nn}\\\\\n",
    "\\end{bmatrix} , b = \\begin{bmatrix}\n",
    "    b_{1}  \\\\\n",
    "    b_{2}  \\\\\n",
    "    b_{3}  \\\\\n",
    "    ...    \\\\\n",
    "    b_{n}  \\\\\n",
    "\\end{bmatrix}$\n",
    "\n",
    "返回 $ Ab = \\begin{bmatrix}\n",
    "    a_{11}    & a_{12} & ... & a_{1n} & b_{1}\\\\\n",
    "    a_{21}    & a_{22} & ... & a_{2n} & b_{2}\\\\\n",
    "    a_{31}    & a_{22} & ... & a_{3n} & b_{3}\\\\\n",
    "    ...    & ... & ... & ...& ...\\\\\n",
    "    a_{n1}    & a_{n2} & ... & a_{nn} & b_{n} \\end{bmatrix}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO 构造增广矩阵，假设A，b行数相同\n",
    "def augmentMatrix(A, b):\n",
    "    rows_A, cols_A = shape(A)\n",
    "    rows_b, cols_b = shape(b)\n",
    "    \n",
    "    if not rows_A == rows_b:\n",
    "        raise IndexError(\"Row number of A must be equal to row number of b\")\n",
    "    \n",
    "    Ab = []\n",
    "    for i in range(rows_A):\n",
    "        row_temp = []\n",
    "        for j in range(cols_A):\n",
    "            row_temp.append(A[i][j])\n",
    "        for k in range(cols_b):\n",
    "            row_temp.append(b[i][k])\n",
    "        Ab.append(row_temp)\n",
    "                           \n",
    "    return Ab\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.010s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 augmentMatrix 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_augmentMatrix"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.2 初等行变换\n",
    "- 交换两行\n",
    "- 把某行乘以一个非零常数\n",
    "- 把某行加上另一行的若干倍："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO r1 <---> r2\n",
    "# 直接修改参数矩阵，无返回值\n",
    "def swapRows(M, r1, r2):\n",
    "    M[r1], M[r2] = M[r2], M[r1]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.005s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 swapRows 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_swapRows"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO r1 <--- r1 * scale\n",
    "# scale为0是非法输入，要求 raise ValueError\n",
    "# 直接修改参数矩阵，无返回值\n",
    "def scaleRow(M, r, scale):\n",
    "    if scale == 0:\n",
    "        raise ValueError(\"Scale parameter must be non-zero value\")\n",
    "    for i,ele in enumerate(M[r]):\n",
    "        M[r][i] = ele * scale\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.006s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 scaleRow 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_scaleRow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO r1 <--- r1 + r2*scale\n",
    "# 直接修改参数矩阵，无返回值\n",
    "def addScaledRow(M, r1, r2, scale):\n",
    "    if scale == 0:\n",
    "        raise ValueError(\"Scale parameter must be non-zero\")\n",
    "    for i, ele in enumerate(M[r2]):\n",
    "        M[r1][i] = M[r1][i] + ele * scale\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 0.003s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 addScaledRow 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_addScaledRow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.3  Gaussian Jordan 消元法求解 Ax = b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3.1 算法\n",
    "\n",
    "步骤1 检查A，b是否行数相同\n",
    "\n",
    "步骤2 构造增广矩阵Ab\n",
    "\n",
    "步骤3 逐列转换Ab为化简行阶梯形矩阵 [中文维基链接](https://zh.wikipedia.org/wiki/%E9%98%B6%E6%A2%AF%E5%BD%A2%E7%9F%A9%E9%98%B5#.E5.8C.96.E7.AE.80.E5.90.8E.E7.9A.84-.7Bzh-hans:.E8.A1.8C.3B_zh-hant:.E5.88.97.3B.7D-.E9.98.B6.E6.A2.AF.E5.BD.A2.E7.9F.A9.E9.98.B5)\n",
    "    \n",
    "    对于Ab的每一列（最后一列除外）\n",
    "        当前列为列c\n",
    "        寻找列c中 对角线以及对角线以下所有元素（行 c~N）的绝对值的最大值\n",
    "        如果绝对值最大值为0\n",
    "            那么A为奇异矩阵，返回None (你可以在选做问题2.4中证明为什么这里A一定是奇异矩阵)\n",
    "        否则\n",
    "            使用第一个行变换，将绝对值最大值所在行交换到对角线元素所在行（行c） \n",
    "            使用第二个行变换，将列c的对角线元素缩放为1\n",
    "            多次使用第三个行变换，将列c的其他元素消为0\n",
    "            \n",
    "步骤4 返回Ab的最后一列\n",
    "\n",
    "**注：** 我们并没有按照常规方法先把矩阵转化为行阶梯形矩阵，再转换为化简行阶梯形矩阵，而是一步到位。如果你熟悉常规方法的话，可以思考一下两者的等价性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3.2 算法推演\n",
    "\n",
    "为了充分了解Gaussian Jordan消元法的计算流程，请根据Gaussian Jordan消元法，分别手动推演矩阵A为***可逆矩阵***，矩阵A为***奇异矩阵***两种情况。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   7,   5,   3,  -5 ||  1  \n",
      "  -4,   6,   2,  -2 ||  1  \n",
      "  -9,   4,  -5,   9 ||  1  \n",
      "  -9, -10,   5,  -4 ||  1  \n"
     ]
    }
   ],
   "source": [
    "# 不要修改这里！\n",
    "from helper import *\n",
    "\n",
    "A = generateMatrix(4,seed,singular=False)\n",
    "b = np.ones(shape=(4,1)) # it doesn't matter\n",
    "Ab = augmentMatrix(A.tolist(),b.tolist()) # please make sure you already correct implement augmentMatrix\n",
    "printInMatrixFormat(Ab,padding=4,truncating=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "请按照算法的步骤3，逐步推演***可逆矩阵***的变换。\n",
    "\n",
    "在下面列出每一次循环体执行之后的增广矩阵。\n",
    "\n",
    "要求：\n",
    "1. 做分数运算\n",
    "2. 使用`\\frac{n}{m}`来渲染分数，如下：\n",
    " - $\\frac{n}{m}$\n",
    " - $-\\frac{a}{b}$\n",
    "\n",
    "增广矩阵\n",
    "$ Ab = \\begin{bmatrix}\n",
    "    7 & 5 & 3 & -5 & 1\\\\\n",
    "    -4 & 6 & 2 & -2 & 1\\\\\n",
    "    -9 & 4 & -5 & 9 & 1\\\\\n",
    "    -9 & -10 & 5 & -4 & 1\\end{bmatrix}$\n",
    "\n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & \\frac{-4}{9} & \\frac{5}{9} & -1 & \\frac{-1}{9}\\\\\n",
    "    0 & \\frac{38}{9} & \\frac{38}{9} & -6 & \\frac{5}{9}\\\\\n",
    "    0 & \\frac{73}{9} & \\frac{-8}{9} & 2 & \\frac{16}{9}\\\\\n",
    "    0 & -14 & 10 & -13 & 0\\end{bmatrix}$\n",
    "    \n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & 0 & \\frac{5}{21} & \\frac{-37}{63} & \\frac{-1}{9}\\\\\n",
    "    0 & 1 & \\frac{-5}{7} & \\frac{13}{14} & 0\\\\\n",
    "    0 & 0 & \\frac{103}{21} & \\frac{-69}{126} & \\frac{16}{9}\\\\\n",
    "    0 & 0 & \\frac{152}{21} & \\frac{-625}{63} & \\frac{5}{9}\\end{bmatrix}$\n",
    "    \n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & 0 & 0 & \\frac{-119}{456} & \\frac{-59}{456}\\\\\n",
    "    0 & 1 & 0 & \\frac{-23}{456} & \\frac{25}{456}\\\\\n",
    "    0 & 0 & 1 & \\frac{-625}{456} & \\frac{35}{456}\\\\\n",
    "    0 & 0 & 0 & \\frac{181}{152} & \\frac{213}{152}\\end{bmatrix}$\n",
    "\n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & 0 & 0 & 0 & \\frac{193}{1086}\\\\\n",
    "    0 & 1 & 0 & 0 & \\frac{62}{543}\\\\\n",
    "    0 & 0 & 1 & 0 & \\frac{1835}{1086}\\\\\n",
    "    0 & 0 & 0 & 1 & \\frac{213}{181}\\end{bmatrix}$\n",
    "\n",
    "$...$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  -7,  -3,   1,  -9 ||  1  \n",
      "   0,   0,   0,   0 ||  1  \n",
      "  -2,   7,   7,  -3 ||  1  \n",
      "   8,  -5,  -6,   3 ||  1  \n"
     ]
    }
   ],
   "source": [
    "# 不要修改这里！\n",
    "A = generateMatrix(4,seed,singular=True)\n",
    "b = np.ones(shape=(4,1)) # it doesn't matter\n",
    "Ab = augmentMatrix(A.tolist(),b.tolist()) # please make sure you already correct implement augmentMatrix\n",
    "printInMatrixFormat(Ab,padding=4,truncating=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "请按照算法的步骤3，逐步推演***奇异矩阵***的变换。\n",
    "\n",
    "在下面列出每一次循环体执行之后的增广矩阵。\n",
    "\n",
    "要求：\n",
    "1. 做分数运算\n",
    "2. 使用`\\frac{n}{m}`来渲染分数，如下：\n",
    " - $\\frac{n}{m}$\n",
    " - $-\\frac{a}{b}$\n",
    "\n",
    "增广矩阵\n",
    "$ Ab = \\begin{bmatrix}\n",
    "    -7 & -3 & 1 & -9 & 1\\\\\n",
    "    0 & 0 & 0 & 0 & 1\\\\\n",
    "    -2 & 7 & 7 & -3 & 1\\\\\n",
    "    8 & -5 & -6 & 3 & 1\\end{bmatrix}$\n",
    "\n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & \\frac{-5}{8} & \\frac{-3}{4} & \\frac{3}{8} & \\frac{1}{8}\\\\\n",
    "    0 & 0 & 0 & 0 & 1\\\\\n",
    "    0 & \\frac{23}{4} & \\frac{11}{2} & \\frac{-9}{4} & \\frac{5}{4}\\\\\n",
    "    0 & \\frac{-59}{8} & \\frac{-17}{4} & \\frac{-51}{8} & \\frac{15}{8}\\end{bmatrix}$\n",
    "    \n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & 0 & \\frac{-23}{59} & \\frac{54}{59} & \\frac{-2}{59}\\\\\n",
    "    0 & 1 & \\frac{34}{59} & \\frac{51}{59} & \\frac{-15}{59}\\\\\n",
    "    0 & 0 & \\frac{129}{59} & \\frac{-426}{59} & \\frac{160}{59}\\\\\n",
    "    0 & 0 & 0 & 0 & 1\\end{bmatrix}$\n",
    "    \n",
    "$ --> \\begin{bmatrix}\n",
    "    1 & 0 & 0 & \\frac{-16}{43} & \\frac{58}{129}\\\\\n",
    "    0 & 1 & 0 & \\frac{119}{43} & \\frac{-125}{129}\\\\\n",
    "    0 & 0 & 1 & \\frac{-142}{43} & \\frac{160}{129}\\\\\n",
    "    0 & 0 & 0 & 0 & 1\\end{bmatrix}$\n",
    "\n",
    "$...$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3.3 实现 Gaussian Jordan 消元法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO 实现 Gaussain Jordan 方法求解 Ax = b\n",
    "\n",
    "\"\"\" Gaussian Jordan 方法求解 Ax = b.\n",
    "    参数\n",
    "        A: 方阵 \n",
    "        b: 列向量\n",
    "        decPts: 四舍五入位数，默认为4\n",
    "        epsilon: 判读是否为0的阈值，默认 1.0e-16\n",
    "        \n",
    "    返回列向量 x 使得 Ax = b \n",
    "    返回None，如果 A，b 高度不同\n",
    "    返回None，如果 A 为奇异矩阵\n",
    "\"\"\"\n",
    "\n",
    "def gj_Solve(A, b, decPts=4, epsilon = 1.0e-16):\n",
    "    #定义一个函数，功能：求矩阵第col列中第i行到第j行的元素的绝对值的最大值,\n",
    "    #同时返回所在的行号,以及col列中所在目标行的元素的实际值\n",
    "    def maxAbs_rowidx_InColumn(M,col,i,j):\n",
    "        max_val = 0 #max value will be found\n",
    "        target_row_idx = i #index will be found\n",
    "        act_val = M[i][col] #actual value will be found\n",
    "        k = i\n",
    "        while k <= j:\n",
    "            val = abs(M[k][col])\n",
    "            if val > max_val:\n",
    "                max_val = val\n",
    "                target_row_idx = k\n",
    "                act_val = M[k][col]\n",
    "            k += 1\n",
    "        return target_row_idx, act_val, max_val\n",
    "    \n",
    "    \n",
    "    rows_A, cols_A = shape(A)\n",
    "    rows_b, cols_b = shape(b)\n",
    "    if not rows_A == rows_b:\n",
    "        return None\n",
    "    Ab = augmentMatrix(A, b)\n",
    "    for c, _ in enumerate(Ab):\n",
    "        tg_row_idx, act_val, max_val = maxAbs_rowidx_InColumn(Ab, c, c, rows_A-1)\n",
    "        if max_val < epsilon:\n",
    "            return None\n",
    "        if not tg_row_idx == c:\n",
    "            #使用第一个行变换，将绝对值最大值所在行交换到对角线元素所在行（行 c）\n",
    "            swapRows(Ab, c, tg_row_idx)\n",
    "            \n",
    "        #使用第二个行变换，将列c的对角线元素缩放为1\n",
    "        scale = 1/act_val\n",
    "        scaleRow(Ab, c, scale)\n",
    "        #多次使用第三个行变换，将列c的其他元素消为0\n",
    "        tg_rows_set = set(t for t in range(rows_A)) - set({c})\n",
    "        for r in tg_rows_set:\n",
    "            scale = -Ab[r][c]\n",
    "            if not abs(scale) < epsilon:\n",
    "                addScaledRow(Ab, r, c, scale)\n",
    "    #四舍五入Ab中每个元素\n",
    "    matxRound(Ab, decPts=4)\n",
    "     \n",
    "    #提取单位矩阵右边的矩阵或向量出来赋给result    \n",
    "    result = []\n",
    "    i = 0\n",
    "    while i < rows_A:\n",
    "        k = cols_A\n",
    "        tmp_row_list = []\n",
    "        while k < len(Ab[0]):\n",
    "            tmp_row_list.append(Ab[i][k])\n",
    "            k += 1\n",
    "        result.append(tmp_row_list)\n",
    "        i += 1\n",
    "       \n",
    "    return result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      ".\n",
      "----------------------------------------------------------------------\n",
      "Ran 1 test in 4.970s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "# 运行以下代码测试你的 gj_Solve 函数\n",
    "%run -i -e test.py LinearRegressionTestCase.test_gj_Solve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## (选做) 2.4 算法正确判断了奇异矩阵：\n",
    "\n",
    "在算法的步骤3 中，如果发现某一列对角线和对角线以下所有元素都为0，那么则断定这个矩阵为奇异矩阵。\n",
    "\n",
    "我们用正式的语言描述这个命题，并证明为真。\n",
    "\n",
    "证明下面的命题：\n",
    "\n",
    "**如果方阵 A 可以被分为4个部分: ** \n",
    "\n",
    "$ A = \\begin{bmatrix}\n",
    "    I    & X \\\\\n",
    "    Z    & Y \\\\\n",
    "\\end{bmatrix} , \\text{其中 I 为单位矩阵，Z 为全0矩阵，Y 的第一列全0}$，\n",
    "\n",
    "**那么A为奇异矩阵。**\n",
    "\n",
    "提示：从多种角度都可以完成证明\n",
    "- 考虑矩阵 Y 和 矩阵 A 的秩\n",
    "- 考虑矩阵 Y 和 矩阵 A 的行列式\n",
    "- 考虑矩阵 A 的某一列是其他列的线性组合"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO 证明："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3  线性回归"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.1 随机生成样本点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nQiABACTy/5/bBCDZ7jqRAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1c181540e48>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 不要修改这里！\n",
    "# 运行一次就够了！\n",
    "from helper import *\n",
    "from matplotlib import pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "X,Y = generatePoints(num=100)\n",
    "\n",
    "## 可视化\n",
    "plt.xlim((-5,5))\n",
    "plt.xlabel('x',fontsize=18)\n",
    "plt.ylabel('y',fontsize=18)\n",
    "plt.scatter(X,Y,c='b')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.2 拟合一条直线\n",
    "\n",
    "### 3.2.1 猜测一条直线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZIAAAESCAYAAADXMlMiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XmczuX+x/HXx1gyyXEMyUlGJ4PIPiptWoScQiqnmiQc\nayKlRVNpQdpEi5iknEx7SZ1KRaSUMghZZ/plprRpVGKs4/r98Z0x2z1jxiz39n4+HvO4577v63vP\nZU7H2/W9rutzmXMOERGRI1XJ3x0QEZHgpiAREZFSUZCIiEipKEhERKRUFCQiIlIqChIRESkVBYmI\niJSKgkREREpFQSIiIqVS2d8dqAh16tRxjRo18nc3RESCyooVK35zztU9XLuwCJJGjRqRlJTk726I\niAQVM0stTjvd2hIRkVJRkIiISKkoSEREpFQUJCIiUioKEhERKRUFiYiIlIqCRERESiU8gmTrVvjr\nL3/3QkQkJIVHkPz8MzRpArNmQWamv3sjIhJSwiNImjWDE0+EgQOhQwf45BMSE6FRI6hUyXtMTPR3\nJ0VEglN4BMnRR8PSpfDSS5CeDueey9H9LiMi9Vucg9RUGDxYYSIiciTCI0gAzODKK2HjRh6uNZ7O\nmR+wnuY8yK3U5E8yMiA+3t+dFBEJPuETJNmqV+e2P+NpwmYSiWMMj5BMDIOZwQ+pmj8RESmp8AsS\noGFD+Il/MJBZdGA5G2nGDIaytkpbWLjQ390TEQkqYRkkEyZAZKT3/Ura04lPuLrq6zSotRM6d4Ye\nPWDzZv92UkQkSIRlkMTFQUICREd7UyfR0ca/Zl3GMWnr4cEHYfFiaNECRo+G338/dJ1WeomIFGTO\nOX/3wSczOwpYAlTDO4DrdefcODOrDbwCNAK2AH2cc78X9jkAsbGxrkQHW/3yC9x9N8ycCbVqwb33\n8uIxQxg0vAoZGTnNIiO9QIqLK9mfTUQkGJjZCudc7OHaBfKIZC9wvnOuNdAG6GZmpwO3AwudczHA\nwqznZatePZgxA1auhDZt4IYb6DCoNWdnzM/TrKQrvTSiEZFQFLBB4jw7s55WyfpyQE9gdtbrs4Fe\n5daJ1q1hwQKYNw/272c+F/EeF3Ey6w81SUsr3kclJnp7VVJT0d4VEQkpARskAGYWYWZfA78CHznn\nvgTqOed+ymryM1CvkGsHm1mSmSVt27atNJ2AHj3o3nAdo5lMR75gDa14nBuoTToNGxbvY+LjyXNb\nDEo+ohERCUQBHSTOuUznXBugAXCqmZ2S732HN0rxdW2Ccy7WORdbt27dUvflnolVSYgcTWNSmMEQ\nhvE0KTTmjbMeg337Dnt9YSOX4o5oREQCVUAHSTbn3B/AIqAb8IuZ1QfIevy1vH9+YmLOiOKPiDqM\n4Cm61V/D7pan0T7xJjjlFHjnHe+eVSEKG7kUd0QjIhKoAjZIzKyumdXK+r46cCGwEXgb6JfVrB8w\nrzz7kXtuA7ziwZGR0P/h5vxjzXx47z2IiPD2nlx4IaxZ4/NzJkyAqlXzvla1qve6iEgwC9ggAeoD\ni8xsDbAcb47kf8Ak4EIzSwY6Zz0vN4ed27joIi88nngCVq2Ctm1hyBD4teBAKf+AJUBXXouIlEjA\n7iMpSyXeR5JLpUq+/8I3g4MH8724fTvcdx8Hn3yKnQcjGe/u5K2GIxk3sRrx8TmjmtwiImD2bO1F\nEZHAEwr7SAJCieY2atcmscMU2lX5hk/cOTzErbyf1pz/DXiT1FTfgZ2ZqWXAIhLcFCSHkbsuV7bI\nyMLnNuLjYfWepvTgHS7kQzKI5KV9l7GI82jLSp/XaBmwiAQzBclhFKzLVXRZlNzLeRdwIW1ZxVCe\npgXrSCKWZxnAcfxU4LrUVI1KRCQ4aY6kjDVq5HsupCZ/Es8EbmQK+6jKRO7gMUazh+qH2qh2l4gE\nEs2R+ImvW2EAO/gbt/EQzVnPh3RhIvFspBl9eIXsPZW6xSUiwUhBUsby3wqLiMj7/rc05jLe5FwW\nsZ3avMKVfMZZdOArQDvdRST4KEjKQVwcbNniLQ+ePdt3m084l1iSGMhMTuJbvuI0ZnMtp/7jhwrt\nq4hIaSlIyllcHERF+X7PWQSzGEgMyUxkLH14lc+2NYF774Vduyq2oyIiR0hBUgGmTvW9hHjoUO8W\n2C47hoToicx/bCOVe10C99wDTZvCnDk+dj2KiAQWBUkFKGwJ8bRpObfAtmyBXjc2gldegU8/hfr1\noW9fOP10+PxzP/8JREQKpyCpILnnTbZsOcwS37POgi+/9CZYtm6FM8+Eq67yva5YRMTPFCSBqlIl\nuPZa2LzZOz9+3jxo1gzuvBN27jz89SIiFURBEuiOPtqbfN+0CS67zNuoEhMDzz2n+RMRCQgKkmBx\nwgne5PuyZd72+QEDoEMHWLLkUJPERO+tSpW8R5VcEZGKoCAJNqed5k2+v/gibNsGnTrB5Zcz77H/\nO3QAl3Peo6oKi0hFUJAEIzNv8n3TJrj/fpg/n243nczdGbdxDDsONVPJFRGpCAqSIJb4ZnUazbyT\n43dt5kWu5jYeIoXGDCKBSmQCXsmV4cOhcmUvfypX9p6LiJQVBUmQyn2W/I/8gwE8R3uS2EgzEhjC\nStpxPguJjISnn/YO0ALv8emnFSYiUnYUJEHK11nyK2lPJz7hcl6jJjtYSGde3NWTxiQXuD4hoYI6\nKiIhT0ESpAqvEmy8weWczAbuqDSJc1nEOlrwKDdRi98PtcoeoYiIlJaCJEgVdpZ8tr0cxQMHbyOG\nZJ7nOm5kCsnEMJyniOBAgfL2IiJHSkESpAo7QCu/X6nHEBJoyyrW0IqnGMFqWjOl2/zy76SIhAUF\nSZA63AFa2aKjYdgwWBfRmgtYyKWV3qLu3/Yx4t2LoHt32LChYjsuIiFHQRLE8h+g5atU/YQJXpXh\nAwfAOWNuZk8WTl3H/X9/lD/e/5wDzVuyqcsNkJ7ulz+DiAQ/BUmIKKxUff4qw4mJ0G9QVe7+/SZi\nSGYGQ2j80TT2NmwMU6bAvn3++QOISNBSkISQ4pSqHzUK9u/3vv+NuozgKVqzmqX7T4XRo9kR3ZIB\nx/6PSuaKVa9L9b1EREESZnzdwVrHKVywfz6LxrzLT78Ys7Zdwgd04ZjUtfTt641wfIVE7k2Rqu8l\nEr7MOefvPpS72NhYl5SU5O9uBASzwt+LjoatqfsZynTuZRx/40+eYRB3cx/bOJYqVaBmTdi+3Vt+\nvHOn72CKjvZGRCIS3MxshXMu9nDtNCIJM1FRhb+elgYHqMKT3EBjUniCGxjIsyQTwxgexvbvJT09\nZ/RR2Px84ZslRSQUBWyQmNkJZrbIzNab2TozG5X1em0z+8jMkrMe/+7vvgaTqVOhatW8r1Wt6r2e\ne5Pj79RmNFM4hW9Ywjk8zK2spzm9mAsUPYqtVEm3t0TCScAGCXAAuNk51xw4HbjezJoDtwMLnXMx\nwMKs51JMcXEwa1be1V2zZnmv+9rkuJmm9OAduvABu6nOXHqziPNow6pCf0ZmpuZKRMJJ0MyRmNk8\n4Mmsr3Odcz+ZWX1gsXOuaVHXao6k+BITvYKQqale0OT+zyOCA/yHmdzPXUSRznP0J54J/MJxPj9L\ncyUiwS2k5kjMrBHQFvgSqOec+ynrrZ+Ben7qVkjKXkLsHLzwghcG2TKpzAyGEkMyk7mJvrxAMjGM\nZSJHsbvAZ2muRCQ8BHyQmFkN4A3gRufcjtzvOW845XNIZWaDzSzJzJK2bdtWAT0NPdmhMmdO3lte\nf1KLW3iE5qxnAZ2ZSDwbOJk+vELu/zkOV1hSREJDQAeJmVXBC5FE59ybWS//knVLi6zHX31d65xL\ncM7FOudi69atWzEdDlG+ds1HRcG3NKY3czmPj/mDWrzClXzK2cSyHPDmXEQk9AVskJiZAc8CG5xz\nk3O99TbQL+v7fsC8iu5bOMq/a37q1JxRymLOoz0rGMhMGpPCck5lNtdSfftWf3ZZRCpIwAYJcCbQ\nFzjfzL7O+uoOTAIuNLNkoHPWc6lg2aOU7KrDB4lgFgNpwmYe4Hb68CpdRzZh9eX3FTzKUURCStCs\n2ioNrdoqP9llUvJnRSO+40Fuow+vQYMGMGkSXHWVt8lERIJCSK3aksCVPTLJbwsn8m9e5RyWQL16\ncM01fF29I2fYFyruKBJiFCRSanFxeZcJ55YWfTaJo75icNXnOXbf93zOGTyQehX3/ydVYSISIhQk\nUiZ87YrPPlgr/q5KPLOvH03YzH3cRS/eYtWeZmwffqdX+VFEgpqCRMpEUQdrZW9M3EUNxnEfTdnE\nm/Tmhh0ToEkTeP55bzmYiAQlBYmUmcIO1sq/MfF7GnINiVx63Bfem/37Q4cOsGRJRXdZRMqAgkTK\nna/bXmbw1s+n0+inL1g6PBG2bYNOneCKK+C773x+jk5jFAlMChIpd7lve0HeYpCpaUaX56/m5Xs2\nwn33wXvvQbNmcNttsCOnIo5OYxQJXNpHIhWqUSMvBPI7VCl461av/PDs2XDssTB+PC8eNYBr+0eQ\nmen7M6OjvVGPrzPqReTIaR+JBKTCKgIfev34473J9+XLvYn4wYNp2a8d52R+XOhnanQi4l8KEqlQ\nhVUELvB6bCwsWcLwOq9Sw+3gYy5gLr1oTLLP6zMyvIGMiFQ8BYlUqKL2mxRgxtO/XcHJbOB2HuAC\nFrKOFjzCzfyNPwo01/knIv6hIJEKVdR+k/yGD/ce93IUD3I7MSQzm36M5jFSaMwwphHBgUPtdf6J\niH8oSKTCFbbfJLfERJg+Pe9rv3Acg3mGdqxkLS2ZxvWspjVd+KDwUY2IlDsFiQSk+Pi858Xntpo2\nnM/H9GIu1djLB3Rjc0x34tptyNMu976TOnW8L+1BESl7ChIJSIef7zDm0YvuDdex4upHOP67pdCy\nJYwcCenpBfadpKd7X9qDIlL2FCQSkIo735GcVo1z3rqZ1yelwODBHHzyKf44NoavrpnKvoz9hV6n\nVV4iZUdBIgHJ1+quwmRkwJgH65J45jROq7aarw7GMpUbWUtL/sX/AN/3yLTKS6RsKEgkIPla3TVn\njve9L2lp3ggjac8pdOWDrACB/3EJH9CVFnxT4Bqt8hIpGwoSCVi+VncVtaExZ4RhvMe/aMlaRjKV\nWJJYTWumMYw6bAMK7l1RQUiRI6cgkaBS1IbG/CFzgCo8wUgak8LMo0YwiGdIoTHjaz3CzKf2Hlp2\nrIKQIqWjIJGgUtSGxsJC5sphtXmg3lRasZYV1c8i/o9buGp8C5g7F5wjPt6bZ8lNk/EixafqvxJS\nEhO9AEhL80Yo3bt7hYRzB0WPah8wu85N1Nq6Hs49l7aLH+Nr2hT4LDMd3CjhTdV/JSzln1d5772C\no42393alfcRqmDYN1q5lBe14hv9Qj5/ztNNkvEjxKEgkpBW2xPe77yvDsGGQksLG7jdxLf8lmRhu\n5wGqsQczbzQjIoenIJGQdtiy9bVq0fzdR5hw1ToW0JkHuIMNnMxl7jVmP+804S5SDAoSCWnFLVs/\n+/MYejOX81nIDmryGn2Yv/scXh5T8rk1LSWWcKMgkZBW3LL12bfAFnE+7VjJf3iGJmzmnZ87QL9+\n3hHAxaClxBKOtGpLBN9nyR/DDh6sOZFhex6DypXhtttgzJgia7cc9kx6kSCiVVsiJeDrFlhmZE1q\nTpsEGzZ4M+/jxkHTpt7wwse64MRE3yECqusloU1BIsJhboH985/w2muwZAkceyxccw2ccQYsW3bo\n+uxbWoXRUmIJZQEdJGY2y8x+NbNvcr1W28w+MrPkrMe/+7OPEjoOe3Lj2WfD8uV8Pvh5fklKg44d\nmXf01cydmuZzd3w2nd4ooS6ggwR4HuiW77XbgYXOuRhgYdZzkQqR+FIlLpzTj5MyN3M/d9IlYy7d\nbmzKwNS7OJqdPq8p7Ex6kVAR0EHinFsCbM/3ck9gdtb3s4FeFdopCWujRnkjj13U4G7upymbmMul\n3MV4NtOEfjyPkTN/Eh2tEJHQF9BBUoh6zrmfsr7/Gajnq5GZDTazJDNL2rZtW8X1TkJWYqJ3XG9u\n39OQOF6kI5/zQ6WGPE9/vuJUzuJT3dKSsBGMQXKI89Yu+1y/7JxLcM7FOudi69atW8E9k1BUVDXg\nn6I7kvz854yKmkM9fuFTzuHT467g6Vu/y7MxUZsVJRQFY5D8Ymb1AbIef/VzfyRMFLWEd8IEiOtb\niam/xXHCrk2svuw+mv7feyz8sRkT3e1sT93BgAHQv782K0roCcYgeRvol/V9P2CeH/siYaSwJbxR\nUfnmQSIj6Zl0F03YzMtcye08SDIx9N03k8z9mXmu1bknEgoCOkjM7CXgC6Cpmf1gZgOBScCFZpYM\ndM56LlLuCqvbNXVqwbZpafAjx3Mds+nAVyQTw0wGsYL2nMfHBdqKBLOADhLn3FXOufrOuSrOuQbO\nuWedc+nOuQucczHOuc7OufyrukTKRXHrdkHe0UsSHTibT+nDK9TiDz7mAubSi8YkF2grEowCOkhE\nAs1hNy1mKTh6MeZV7UPLyhsZy0QuYCHraMHUyjfz0B1/lH/HRcqRgkSkHPgavcyaBdOfP4qXosfS\nhGTerHEtN2Q+Rp/4GHj6aThwwN/dFjkiJar+a2abgWeB2c65nw/XPlCo+q8ErFWr4KabYPFiaNEC\nJk+GLl383SsRoPyq/+4HHgDSzOwtM7vYzDSqETlSbdvCxx/D3LmwZw907QoXXwwbN/q7ZyLFVqIQ\ncM61AM7AK01yHt7S2+/NbIKZnVQO/RMJfWbQqxesWwcPPwyffgotW7Kx60janJCuzYsS8Eo8mnDO\nLXPODQLqA/8BvgPGApvN7GMzu9rMqpVxP0VCX7Vq3sFZycls7vQfYj58io9/iGGEe5ytqfu1eVEC\n1hHflnLOZTjnnnPOnQU0A14GzgVeAH40sylmpoWNIiV17LF0SXmaNnzNCtrzOKNYS0vOzXiX+DtC\n/0RTCT6lmt8wswgzuxSYDPwbr+7VImAZMALYYGY9S91LkTCTlgbf0JIufMjFvIPheJeLSUjrCt98\nU6C9aniJPx1RkJhZMzN7GNgKvAHEAo8ATbI2Cf4Lb5SyCXiorDorEi5yNika73IxLVnLKKZwaqUk\naN0ahg+HrKrW2aczqoaX+EuJgsTMBprZUmAdcBOwGrgCaOCcu9059212W+dcCvA4oEl4kRLKv6Fx\nP1V5nFF0+FsyGztf721SiYmBRx/lnjv2FTidUTW8pCKVdETyDHAiXn2rk5xzXZ1zbzjnCttJtR5v\nzkRESiB7Q2NUVN7XU36Pov1nj/POxLVw5pkwZgzvpzWnJ2+R/0SF1NSK66+Et5IGSW/gBOdcvHNu\ny+EaO+e+cs71P6KeiYS5uDioUaPg6xkZcMO0k0m8+l16VpvPXqrxFpfyMefTmq/ztK1TR7e4pPyV\ndB/JW865zMO3FJGyUFhl4LQ079bV23u70prVDOcpWrKWlbQjgUHUwys8kZ6eM1+iCXkpLyUqkRKs\nVCJFglWjRr5vUUVHe2GS+/++tfidOxnPSB5nD0cxgXimcCN7OYqoKNi9mzxzKZGRhVcvFoHyK5Ei\nIhWosDNQJkwoWH7+D/7OGB6lOetZyAVMYiwbOJnLeY30dKcJeSk3ChKRAFbUGSi+QgYghRgu5S3O\nZyE7qMlr9GEJ59COFQXa6lAtKQsKEpEAV9gZKPlDJioKjj4657pFnE87VjKIBJqwmRXE8hzXUZ8f\nD7XRoVpSFhQkIkEsd8j89hvs3Alz5njhAnCQCGYyiBiSmcRtXMVLJBPDndxPVPUMJkzwa/clRChI\nREJMdrhkhwnAX9RkLJM4mQ28R3fu525Sqzclzl7MO2NfBK36ksIoSERClK85lF8i/8m+Oa/BJ59w\ndHRdL3XOOAOWLSvys1SGRYqiIBEJUUVN1HPOOZCUBM895w1fOnb03vj+e5+fFR+PVn1JoRQkIiGs\nsIl6wLtHdd11kJzsJcKbb0LTpnD33d5kSy5FbYz0RbfBwouCRCTc1agB48fDpk3eSY33309Gw6bc\nXGc2EXaQRo2gdm3fl+Ze9ZUdHmbQt2/e22B9+3oFiyU0KUhExNOwIbz4Ih/cvZR1fzbg0fTrWMZp\nNEj9jPR0qFw5b/PsjZGQdw4FCs7fOwfTp2tkEqoUJCKSx5DZZ3DawS+IYw71+YnPOJtX6EODA98R\nFeVjvgXfcyj5Oac5lVClWlsikkelSjkjikh2MYZHuJWHiCCTWTVHM/z7sVCzZqHXFMXMm6+R4KBa\nWyJyRHLPe2RwNPcxjqZs4lX6MHzHJGjSBGbOhMxMn9cU97MldChIRCSPCRO8kUNuW2lAP/5Lz+O+\nhJNOgkGDoH17WLTo0DW+6n7llntORUKLgkRE8oiLg6FDC4ZJZCT0eeRU+OwzeOUV+OMPOP98uPRS\n4k5LKbBnZdiwQvawSMgJ2jkSM+sGTAUigJnOuUmFtdUciUjJJSZ6k+Npad4tqQkT8gXB7t0wZQpM\nnAh798LIkXDnnVCrlt/6LGUrpOdIzCwCeAq4CGgOXGVmzf3bK5HQUuRmRoDq1WHsWNi82dsoMnky\nxMTA00/DgQOHmmlzYugLyiABTgVSnHP/55zbB7wM9PRzn0TCU/368OyzsGIFtGjh7Txs0wY+/FA1\nusJEsAbJ8UDuokA/ZL0mIv7Stq03+f7GG95tr65dOW7QxZyQsTFPs+waXRqphI5gDZLDMrPBZpZk\nZknbtm3zd3dEwoMZ9O4N69fDQw/RYfcS1tKSKYzi72w/1Cx7ZKKRSmgI1iDZCpyQ63mDrNcOcc4l\nOOdinXOxdevWrdDOiYS9atXglls4r0EKzzKQETxJCo0ZwRNUZj8REb6rCY8apVFKMArWIFkOxJjZ\niWZWFbgSeNvPfRKRfG6adCw3R06nDV+zgvY8wUjWWiu6Zr4LFFwxmp6uUUowCsogcc4dAEYAHwAb\ngFedc+v82ysRyS/7TJS/olvSlQ8ZUPcd/lHvIO9yMfPpRnOK/r+tzjwJDkG7j6QktI9EJIDs20fS\nwGmcNOdearKDGQxhHPfyG75vQas+l/+E9D4SEQliVasS+8KNLHg6hReOGc5gEkixGEYzmSrsK9Bc\n9bkCn4JERPziiqFRXLfjCSqvW8PKamcwmZtZRwt6MI/c8yfdu3uPWi4cuBQkIuJfzZtzwd736Mb7\n7KcK8+jFQi6gFasBmD3b2+Oo5cKBS0EiIn7XsCF8QDdasYbreZJWrGEVbUlgEDUyfiEhwfdyYU3E\nBwYFiYj4XXYZ+kwqM43riSGZKdzIdTxPMjGMyZxENfYUuC41Vbe6AoGCRET8LnuZcESE9/wP/s7N\nTKYF61jEeUxiLBs4mct4nfz7T3Sry/8UJCISEOLivPmQ3AdkJdOEqyPnMfWSBeyyY3idK/iETrRj\nRYHrs3fGS8VTkIhIwMgemeQ/EGvU2xewZvYqxtaeQTM2spwOzKI/9fkxz/Xp6RqV+IM2JIpIcPnz\nT6Y3nEj/HVPYTxUmcTuPcjN7qA5AVBT89puf+xgitCFRRELT3/7GMdMe5GQ2MJ9ujOcuNtGUK3kJ\ncKSn+7uD4UdBIiJBJy4OvuOfXMHrdGIxv1GHl7iazzmD01jm7+6FHQWJiASlqCjvcQmd6MBy+jOL\nRmxhGR29pPneO/tOO+LLn4JERILS1KlQtar3/UEieJ7+nFJlM9/0jPdOaWzalDW9x3HjoF0+d8Qr\nYMqOgkREglJcHMyalXeF1+PPHcMpb42HTZugRw9azb2Pr3c3oS//xfBKCGcvE1bJlbKjVVsiErLO\nsqVMZjSnspzlxHIjU/icMwttHx0NW7ZUXP8CnVZtiUjY+yH6TE5nGdfwAvX5iaWcxcv8m2i2+Gyf\nllax/QsVChIRCVkTJkD1yEokcg1N2cQ47uES3mEjzZjAHdTgrzztdfbJkVGQiEjIyr1Tfrcdzezo\nccyfupkfz+rDHTxAMjEM4FkqkUnVql7wSMkpSEQkpMXFefMeBw96j71HNuCLof/ljIgv+T/+ybP8\nhyRiOTtzsZ97GrwUJCISduLj4YvMUzmTpfybl6nNdhZknkfUoN7w7bf+7l7QUZCISNjJmVQ3XuXf\nNGMj8YznrN0fwsknwy23wJ9/+rOLQUVBIiJhJ/+k+h6qM5F4zjs+Ga65Bh59FGJiYPp0OHDAP50M\nIgoSEQk72Scy5hYZCTc+WN/b5ZiU5I1Mhg2DNm3go4+K/dnhuGNeQSIiYaewc0/i4rIatGsHixd7\npVYyMqBLF7jkEm/HfBESE8Nzx7x2touIFGXvXnj8cbj/fti9G66/Hu6+G2rXLtC0USMvPPIL1h3z\n2tkuIlIWqlXzJt+Tk2HAAHjiCW/+5IknYP/+PE0L2xkf6jvmFSQiIsVRrx7MmAGrVvFT/bYwciQb\nqrbi3zXfp04d7xZZYTd4Qn3HvIJERKQEEte2ovH/fUQP5hFBJq/81Z056d1ozjqf7atUgZ07Q3vy\nXUEiIlIC8fGQsdt4hx6cwjeMZjKn8SWrac2TXE8UOQfGm3lf6emhPfmuIBERKYHc8x37qcoURhND\nMtMZyhBmkEJjRjOZKuzDOdi3L+/1GRleGIWSgAwSM7vCzNaZ2UEzi8333lgzSzGzTWbW1V99FJHw\n5Gu+I5063MCTtGINX9CRydzMOlrQg3lAwYmTUJt8D8ggAb4BegNLcr9oZs2BK4EWQDdgmplFVHz3\nRCRc+drMmG0DzenO+1zEe+ynCvPoxQI605I1edr5WDkc1AIySJxzG5xzvnb+9AReds7tdc59B6QA\np1Zs70QknGVvZoyKKrzNfC6iNau5wZ6gDV+zirbMYDDH8gsAv/8eWpPvARkkRTge+D7X8x+yXivA\nzAabWZKZJW3btq1COici4SEuDmrUKLrNAarwpBtBY1J4nJH05zmSieEWHqLywb0hNfnutyAxswVm\n9o2Pr55l8fnOuQTnXKxzLrZu3bpl8ZEiIocUd57jD/7OTTzGKXzDYs7lIW5jAydzGa8DLiQm3/0W\nJM65zs65U3x8zSvisq3ACbmeN8h6TUSkQpV0k+FmmtKTt+nMR+ziaF7nCj6hE+1YEfST78F2a+tt\n4Eozq2aoGVAVAAAJpUlEQVRmJwIxwFd+7pOIhKGiJt19MYOICFhIZ9qyiiFMpxkbWU4HXonsDz/+\nmKd9MFURDsggMbNLzewHoCPwrpl9AOCcWwe8CqwH5gPXO+cy/ddTEQlXvioIFzYBHx3tHfU7e7YX\nPplUJoEhxJDM1Mpj6L0nEZo0gfHjYffu4Ksi7JwL+a/27ds7EZHyNmeOc5GRznl//XtfkZHe67nb\nREc7Z+Y9zpnjnEtJca53b++Chg3diDovOTiY53PAuagoH9eWIyDJFePvWJWRFxEpQ4mJ3uR5Wpo3\njzJhQq5zTg5n8WIYPRq+/prP6choHuMrTiu0eWRkvnNUylhxy8grSEREAklmJrfUm81N6fHU52fm\nEMdYHuCHPOuMcpTnWSc6j0REJBhFRNBm6gDaVN/MBO7gcl5nE025h3FEsqtA80BY8aUgEREJMHFx\ncNl1x3CXTaAZG3mbHozjPjbThGt4AePgobbFLbdSnqvAFCQiIgHovfeyVmzRiKt4mTP5jK0czwtc\ny5ecxhksBeCvvw4fCuW9CkxBIiISgPLfsvqcMzmdZfTlv/yDH1nKWbzMv6m/b8thd8bHx3vl63Mr\nyx31ChIRkQDka+e8oxJz6EsTNnMP47iEd9hIMwanxntDE3zfwirvs+QVJCIiAcjXznkz7zGDo7mX\ne2jKJl7ncu5gIjRpwheDZzF0UGaBW1iF7cAvq7PkFSQiIgHI1875oUPzhsIPnMCQyDnMv2cZnHgi\nHZ8ZyJLdsXRi8aE2GRmwq+BiL6pU8cKqLChIREQCVFyct0fk4EHvcdq0guGSkADdxp0GS5dyFS9R\nm+0s5jzeoDf/5NtCPzuzDItLKUhERIJI/nA5tKvdjI+irqQZG4lnPF34kPU050FupSZ/FvicgwfL\nbuWWgkREJETs3Al7qM5E4mnCZhKJYwyPkEwMg5lBBAfytC+rlVsKEhGREJCYCHv35jz/iX8wkFl0\nYDkbacYMhrKSdlzAgjzXlcXKLQWJiEgIKGxksZL2dOITrqj0OjXYyQIu5G0uoQmbgLJZuaUgEREJ\nAUWNLKKijF7/vYzY6uu5lQfpxCd8wyk8UXk0D9/xe6l/toJERCQEFFVza+pUb1L+iWeO4tXoW2lC\nMq/VGMDwzMe5YmxjePJJ2L//iH+2gkREJMglJh7a2F7AsGE5K7uyV3z97Opx9V8zqLRqJbRpAzfc\nAK1awfvvH9HPV5CIiAS5+HjYt6/g61FR3t6TQrVuDQsWwLx5cOAAdO8OF10E69eX6OcrSEREglxh\n8yPbtxfjYjPo0QPWrYPJk+GLL7zRyYgRxf75ChIRkSBX2MqrEq3IqlrVO+Y3JQWGDIHp04t9qYJE\nRCTI+SrwGBmZt5ZWsQ+2qlMHnnoKVq8u9s9XkIiIBDlfBR4TEnIm2Yt7sFWesPlXC6BOsc5fNOdc\n2f6JAlBsbKxLSkrydzdERPyiUSMvPPKLjvZWcUFO2OQ9AKv9QedWRBzu8zUiEREJccU52MrXKYpg\nxcoIBYmISIgrzmR8aWpuKUhEREJccSbjS1NzS0EiIhLiDjcZD77DBtzB4nx+5bLrqoiIBKq4uLzB\n4et98OZK0tK8EUpqapqPKfqCNCIRERGg4OmL8Ftx9sYHZpCY2cNmttHM1pjZXDOrleu9sWaWYmab\nzKyrP/spIiIBGiTAR8ApzrlWwGZgLICZNQeuBFoA3YBpZnbYNc4iIlJ+AjJInHMfOueyDxdeBjTI\n+r4n8LJzbq9z7jsgBTjVH30UERFPQAZJPgOA7CL5xwPf53rvh6zXRETET/y2asvMFgDH+Xgr3jk3\nL6tNPHAAKKy8WFGfPxgYDNCwLA4lFhERn/wWJM65zkW9b2bXARcDF7icgmBbgRNyNWuQ9Zqvz08A\nEsCrtVXa/oqIiG8BeWvLzLoBtwI9nHO5q7+8DVxpZtXM7EQgBvjKH30UERFPoG5IfBKoBnxkZgDL\nnHNDnXPrzOxVYD3eLa/rnXOZfuyniEjYC4sy8ma2DSjWDs1yVgf4zd+dCBD6XeTQ7yKHfhc5AuF3\nEe2cq3u4RmERJIHCzJKcc7H+7kcg0O8ih34XOfS7yBFMv4uAnCMREZHgoSAREZFSUZBUrAR/dyCA\n6HeRQ7+LHPpd5Aia34XmSEREpFQ0IhERkVJRkPiJmd1sZs7M6vi7L/5Q1FEB4cLMumUdh5BiZrf7\nuz/+YmYnmNkiM1tvZuvMbJS/++RvZhZhZqvM7H/+7ktxKEj8wMxOALoAaf7uix/5PCogXGQdf/AU\ncBHQHLgq65iEcHQAuNk51xw4Hbg+jH8X2UYBG/zdieJSkPjHY3glYMJ2gqqIowLCxalAinPu/5xz\n+4CX8Y5JCDvOuZ+ccyuzvv8L7y/QsK3qbWYNgH8BM/3dl+JSkFQwM+sJbHXOrfZ3XwJI7qMCwoWO\nRPDBzBoBbYEv/dsTv5qC9w/Ng/7uSHEFaq2toFZUiXzgDrzbWiGvvI8KkNBiZjWAN4AbnXM7/N0f\nfzCzi4FfnXMrzOxcf/enuBQk5aCwEvlm1hI4EVidVYyyAbDSzE51zv1cgV2sEEd4VEC4KPaRCOHA\nzKrghUiic+5Nf/fHj84EephZd+AooKaZzXHOXePnfhVJ+0j8yMy2ALHOOX8XZqtwWUcFTAY6Oee2\n+bs/Fc3MKuMtMrgAL0CWA1c759b5tWN+YN6/qmYD251zN/q7P4Eia0Qyxjl3sb/7cjiaIxF/eRI4\nBu+ogK/NbLq/O1SRshYajAA+wJtcfjUcQyTLmUBf4Pys/xa+zvoXuQQJjUhERKRUNCIREZFSUZCI\niEipKEhERKRUFCQiIlIqChIRESkVBYmIiJSKgkREREpFQSIiIqWiIBERkVJRkIhUIDOrbGZLzWyX\nmTXL997grFMz7/NX/0SOhEqkiFQwM4sGvgZSgdOcc3vNrAVe4cYVwLnOuUx/9lGkJDQiEalgzrlU\nYCDQGnjUzKoDrwB7gDiFiAQbjUhE/MTMpgHDgM+BM4DLwvwsDglSChIRPzGzo4BvgJOAZ5xzg/3c\nJZEjoltbIv7TGmiY9f0pWYddiQQdBYmIH5hZTeAl4DcgHugI3OvXTokcIf0LSMQ/EoBo4ELn3Mdm\n1ha43cwWOOcW+blvIiWiORKRCmZmA4GZwETnXHzWa7XwlgRXAVo559L92EWRElGQiFSgrE2IK/BC\no1PW2e3Z73UElgDvO+d6+KmLIiWmIBERkVLRZLuIiJSKgkREREpFQSIiIqWiIBERkVJRkIiISKko\nSEREpFQUJCIiUioKEhERKRUFiYiIlIqCRERESuX/AQKG/m5L0p2eAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1c1828ce6a0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#TODO 请选择最适合的直线 y = mx + b\n",
    "# m = 0\n",
    "# b = 0\n",
    "# m = -5\n",
    "# b = 8\n",
    "\n",
    "m = -4.94\n",
    "b = 8.14\n",
    "\n",
    "# 不要修改这里！\n",
    "plt.xlim((-5,5))\n",
    "x_vals = plt.axes().get_xlim()\n",
    "y_vals = [m*x+b for x in x_vals]\n",
    "plt.plot(x_vals, y_vals, '-', color='r')\n",
    "\n",
    "plt.xlabel('x',fontsize=18)\n",
    "plt.ylabel('y',fontsize=18)\n",
    "plt.scatter(X,Y,c='b')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.2 计算平均平方误差 (MSE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "我们要编程计算所选直线的平均平方误差(MSE), 即数据集中每个点到直线的Y方向距离的平方的平均数，表达式如下：\n",
    "$$\n",
    "MSE = \\frac{1}{n}\\sum_{i=1}^{n}{(y_i - mx_i - b)^2}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.8466438404992873\n"
     ]
    }
   ],
   "source": [
    "# TODO 实现以下函数并输出所选直线的MSE\n",
    "\n",
    "def calculateMSE(X,Y,m,b):\n",
    "    sum_squared = 0\n",
    "    mse = 0\n",
    "    n = len(X)\n",
    "    for x, y in zip(X, Y):\n",
    "        sum_squared = sum_squared + (y - m*x - b)**2\n",
    "    mse = sum_squared/n  \n",
    "    return mse\n",
    "\n",
    "print(calculateMSE(X,Y,m,b)) \n",
    "# #---------------------------------------\n",
    "# print(calculateMSE(X,Y,-5, 8.2))\n",
    "# print(calculateMSE(X,Y,-5, 8.18))\n",
    "# print(calculateMSE(X,Y,-5, 8.16))\n",
    "# print(calculateMSE(X,Y,-5, 8.14)) #b = 8.14时最小\n",
    "#print(calculateMSE(X,Y,-5, 8.12))\n",
    "##-------------------------------\n",
    "# print(calculateMSE(X,Y,-4.90, 8.14))\n",
    "# print(calculateMSE(X,Y,-4.92, 8.14))\n",
    "# print(calculateMSE(X,Y,-4.94, 8.14))#m = -4.94时最小\n",
    "# print(calculateMSE(X,Y,-4.96, 8.14))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.3 调整参数 $m, b$ 来获得最小的平方平均误差\n",
    "\n",
    "你可以调整3.2.1中的参数 $m,b$ 让蓝点均匀覆盖在红线周围，然后微调 $m, b$ 让MSE最小。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.3 (选做) 找到参数 $m, b$ 使得平方平均误差最小\n",
    "\n",
    "**这一部分需要简单的微积分知识(  $ (x^2)' = 2x $ )。因为这是一个线性代数项目，所以设为选做。**\n",
    "\n",
    "刚刚我们手动调节参数，尝试找到最小的平方平均误差。下面我们要精确得求解 $m, b$ 使得平方平均误差最小。\n",
    "\n",
    "定义目标函数 $E$ 为\n",
    "$$\n",
    "E = \\frac{1}{2}\\sum_{i=1}^{n}{(y_i - mx_i - b)^2}\n",
    "$$\n",
    "\n",
    "因为 $E = \\frac{n}{2}MSE$, 所以 $E$ 取到最小值时，$MSE$ 也取到最小值。要找到 $E$ 的最小值，即要找到 $m, b$ 使得 $E$ 相对于 $m$, $E$ 相对于 $b$ 的偏导数等于0. \n",
    "\n",
    "因此我们要解下面的方程组。\n",
    "\n",
    "$$\n",
    "\\begin{cases}\n",
    "\\displaystyle\n",
    "\\frac{\\partial E}{\\partial m} =0 \\\\\n",
    "\\\\\n",
    "\\displaystyle\n",
    "\\frac{\\partial E}{\\partial b} =0 \\\\\n",
    "\\end{cases}\n",
    "$$\n",
    "\n",
    "### 3.3.1 计算目标函数相对于参数的导数\n",
    "首先我们计算两个式子左边的值\n",
    "\n",
    "证明/计算：\n",
    "$$\n",
    "\\frac{\\partial E}{\\partial m} = \\sum_{i=1}^{n}{-x_i(y_i - mx_i - b)}\n",
    "$$\n",
    "\n",
    "$$\n",
    "\\frac{\\partial E}{\\partial b} = \\sum_{i=1}^{n}{-(y_i - mx_i - b)}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO 证明:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3.2 实例推演\n",
    "\n",
    "现在我们有了一个二元二次方程组\n",
    "\n",
    "$$\n",
    "\\begin{cases}\n",
    "\\displaystyle\n",
    "\\sum_{i=1}^{n}{-x_i(y_i - mx_i - b)} =0 \\\\\n",
    "\\\\\n",
    "\\displaystyle\n",
    "\\sum_{i=1}^{n}{-(y_i - mx_i - b)} =0 \\\\\n",
    "\\end{cases}\n",
    "$$\n",
    "\n",
    "为了加强理解，我们用一个实际例子演练。\n",
    "\n",
    "我们要用三个点 $(1,1), (2,2), (3,2)$ 来拟合一条直线 y = m*x + b, 请写出\n",
    "\n",
    "- 目标函数 $E$, \n",
    "    $$\n",
    "        E = \\frac{1}{2}[(1 - 1m - b)^2 + (2 - 2m - b)^2 + (2 - 3m - b)^2]\n",
    "    $$\n",
    "- 二元二次方程组，\n",
    "- 并求解最优参数 $m, b$\n",
    "    $$\n",
    "    \\begin{cases}\n",
    "    \\displaystyle\n",
    "    -1(1 - 1m - b)-2(2 - 2m - b)-3(2 - 3m - b) = 0 \\\\\n",
    "    \\\\\n",
    "    \\displaystyle\n",
    "    -(1 - 1m - b) - (2 - 2m - b) - (2 - 3m - b) = 0 \\\\\n",
    "    \\end{cases}\n",
    "    $$\n",
    "    \n",
    "    $$\n",
    "     --> \\begin{cases}\n",
    "    \\displaystyle\n",
    "    m = \\frac{1}{2} \\\\\n",
    "    \\\\\n",
    "    \\displaystyle\n",
    "    b = \\frac{2}{3} \\\\\n",
    "    \\end{cases}\n",
    "    $$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO 写出目标函数，方程组和最优参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3.3 将方程组写成矩阵形式\n",
    "\n",
    "我们的二元二次方程组可以用更简洁的矩阵形式表达，将方程组写成矩阵形式更有利于我们使用 Gaussian Jordan 消元法求解。\n",
    "\n",
    "请证明 \n",
    "$$\n",
    "\\begin{bmatrix}\n",
    "    \\frac{\\partial E}{\\partial m} \\\\\n",
    "    \\frac{\\partial E}{\\partial b} \n",
    "\\end{bmatrix} = X^TXh - X^TY\n",
    "$$\n",
    "\n",
    "其中向量 $Y$, 矩阵 $X$ 和 向量 $h$ 分别为 :\n",
    "$$\n",
    "Y =  \\begin{bmatrix}\n",
    "    y_1 \\\\\n",
    "    y_2 \\\\\n",
    "    ... \\\\\n",
    "    y_n\n",
    "\\end{bmatrix}\n",
    ",\n",
    "X =  \\begin{bmatrix}\n",
    "    x_1 & 1 \\\\\n",
    "    x_2 & 1\\\\\n",
    "    ... & ...\\\\\n",
    "    x_n & 1 \\\\\n",
    "\\end{bmatrix},\n",
    "h =  \\begin{bmatrix}\n",
    "    m \\\\\n",
    "    b \\\\\n",
    "\\end{bmatrix}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO 证明:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "至此我们知道，通过求解方程 $X^TXh = X^TY$ 来找到最优参数。这个方程十分重要，他有一个名字叫做 **Normal Equation**，也有直观的几何意义。你可以在 [子空间投影](http://open.163.com/movie/2010/11/J/U/M6V0BQC4M_M6V2AJLJU.html) 和 [投影矩阵与最小二乘](http://open.163.com/movie/2010/11/P/U/M6V0BQC4M_M6V2AOJPU.html) 看到更多关于这个方程的内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4 求解 $X^TXh = X^TY$ \n",
    "\n",
    "在3.3 中，我们知道线性回归问题等价于求解 $X^TXh = X^TY$ (如果你选择不做3.3，就勇敢的相信吧，哈哈)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-4.837 8.126\n"
     ]
    }
   ],
   "source": [
    "# TODO 实现线性回归\n",
    "'''\n",
    "参数：X, Y\n",
    "返回：m，b\n",
    "'''\n",
    "from copy import deepcopy\n",
    "\n",
    "def linearRegression(X,Y):\n",
    "    #为了不改变X和Y, 把它们分别深度赋值给A和B,然后对A和B操作\n",
    "    A = deepcopy(X)\n",
    "    B = deepcopy(Y)\n",
    "    \n",
    "    rows_A, cols_A = shape(A)\n",
    "    rows_B, cols_B = shape(B)\n",
    "    \n",
    "    #如果X不是矩阵形式的列表，就转换X的格式\n",
    "    if cols_A == 0:\n",
    "        for i in range(len(A)):\n",
    "            A[i] = list([A[i]] + [1])\n",
    "            \n",
    "    #如果Y不是矩阵形式的列表，就转换Y的格式\n",
    "    if cols_B == 0:\n",
    "        for i in range(len(B)):\n",
    "            B[i] = list([B[i]])\n",
    "\n",
    "    \n",
    "    #求A的转置AT\n",
    "    AT = transpose(A)\n",
    "    \n",
    "    #求矩阵相乘ATA,A的转置AT乘以A\n",
    "    ATA = matxMultiply(AT, A)\n",
    "    \n",
    "    #创建一个单位矩阵，行列大小与 ATA一样\n",
    "    I = []\n",
    "    rows_ATA, cols_ATA = shape(ATA)\n",
    "    for i in range(rows_ATA):\n",
    "        tmp_row_list = []\n",
    "        for j in range(cols_ATA):\n",
    "            if i == j:\n",
    "                tmp_row_list.append(1)\n",
    "            else:\n",
    "                tmp_row_list.append(0)\n",
    "        I.append(tmp_row_list)\n",
    "    \n",
    "    #利用消元函数gj_Solve()返回增广矩阵[ATA I]消元后的右边的矩阵,即可得到 A 的逆矩阵A_1\n",
    "    A_1 = gj_Solve(ATA, I)\n",
    "    \n",
    "    #求矩阵相乘ATB\n",
    "    ATB = matxMultiply(AT, B)\n",
    "      \n",
    "    #A的逆矩阵A_1乘以矩阵ATB就是最后的解的矩阵\n",
    "    Beta = matxMultiply(A_1, ATB)\n",
    "                                       \n",
    "    return round(Beta[0][0],4), round(Beta[1][0],4)\n",
    "    \n",
    "\n",
    "m,b = linearRegression(X,Y)\n",
    "print(m,b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你求得的回归结果是什么？\n",
    "请使用运行以下代码将它画出来。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n9eptvE2bNj579uzt9mXw4MH+8ssv+zfffFPsiGTLli1+3nnneZ06dTwmJsbHjx9f4PmuXbt6rVq1\nvHfv3kX+x75mzRo/+OCDC1xPSkryhx9+2E899VR/9tlnS+xfaUYky5Yt84YNG3qDBg28Xr16vmbN\nmmLblvRzurufdtpp/swzz7i7+5gxY/zCCy/0rl27ert27fy+++4rsR/lhVKOSML+R748PhQkItvx\n22/eOTbWP6tWzd/YYw8/Oz7ePSMj72kzD3mbzCz02xUOsOTkz/30009395L/wL7//vt+wQUX+JYt\nW/yHH37wpk2b+ldffVWgzZ9//uk9e/b0t956q8D1kSNH+pVXXlng2s8//+z16tXznj17hvx+Gzdu\n9DZtgjBs2LChH3TQQXmPlyxZUqT9VVdd5S+++KK7uz/33HPeqVOn0L+A7fyc99xzj5911lmenXNL\n8f777/fGjRv7hg0bfPPmzd6xY0d/++23i33v8qIgUZCI7JDp06f71X37+rn16/vr4OuqNPTzmOaQ\n7VWq+HZHJLnhEbTLzNfuNofHPCbmEI+NjfX69et79erVQ84vDBo0yP/973/nPe7Xr58/99xzRdpN\nmTLFr7jiigLX2rZt6x988EGBa0uWLPEGDRr4Mccc41lZWSX+/KUZkey77755f/yzs7N9n332KbZt\ncUEyefJk79ixo2/evDnv2rRp07xPnz55j++66y4fNWpUiX0pD6UNEs2RiAgAPXr04M2PPmLelho8\nWOVtNmTXZjq9eZ9/0iE7d3XSe8AiYBExMYuYPHkRixYt4ocfOufNoQSq5rWDu4DL+fPP/5GcvIb3\n33+fpk2b8s477xTpQ6NGjZg7dy4Amzdv5uOPP6Z58+b88ccffP/990AwR/L666/TvHnzvNetWLGC\nX375haOPPjrvWmZmJhdffDHTpk2jRYsWjB49usy/o3r16jF//nwA5s6dS1xc3A69/s0332TUqFG8\n+uqrxOSbwDr55JP54osvyMjIIDMzk/nz5xMfH1/m/pab0qRNtH9oRCJSOgMGDPB9973Jwb0KmX4x\nE309B7qDT+Eib1jl25DzLcWtECv8ERtb9H/qM2bM8Ntuu83d3X///Xfv1auXx8fHe4sWLfL+V75+\n/XpPTEz0Vq1aeUJCgl955ZV5q5/c3e+44w6/6aabCvwsw4cP92uvvdbd3Tdt2uTNmjXzZcuWFfuz\n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\n9MDHSAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1c1824a52e8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 请不要修改下面的代码\n",
    "x1,x2 = -5,5\n",
    "y1,y2 = x1*m+b, x2*m+b\n",
    "\n",
    "plt.xlim((-5,5))\n",
    "plt.xlabel('x',fontsize=18)\n",
    "plt.ylabel('y',fontsize=18)\n",
    "plt.scatter(X,Y,c='b')\n",
    "plt.plot((x1,x2),(y1,y2),'r')\n",
    "plt.text(1,2,'y = {m}x + {b}'.format(m=m,b=b))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你求得的回归结果对当前数据集的MSE是多少？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9341065862961798\n"
     ]
    }
   ],
   "source": [
    "mse = calculateMSE(X,Y,m,b)\n",
    "print(mse) #mse = 0.9341"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
